﻿#include "httpcontext.h"
#include "util.h"
#include "buffer.h"
#include <iostream>
#include <regex>

bool HttpContext::parseRequestLine(const std::string &line)
{
    std::smatch matches;
    std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
    bool ret = std::regex_match(line, matches, e);
    if (ret == false)
    {
        m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
        m_respStatu = 400; // BAD REQUEST
        return false;
    }
    // [1] 请求方法的获取, mathes下标从1开始是匹配的部分
    m_request.m_method = matches[1];
    std::transform(m_request.m_method.begin(), m_request.m_method.end(), m_request.m_method.begin(), ::toupper);
    // [2] 资源路径的获取，需要进行URL解码操作，但是不需要+转空格
    m_request.m_path = Util::urlDecode(matches[2], false);
    // [3] 查询字符串的获取与处理
    std::vector<std::string> query_string_arry;
    std::string query_string = matches[3];
    /*查询字符串的格式 key=val&key=val 先以 & 符号进行分割，得到各个字串*/
    Util::split(query_string, "&", &query_string_arry);
    /*针对各个字串，以 = 符号进行分割，得到key和val,得到之后也需要进行URL解码*/
    for (auto &str : query_string_arry)
    {
        size_t pos = str.find("=");
        if (pos == std::string::npos)
        {
            m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
            m_respStatu = 400; // BAD REQUEST
            return false;
        }
        std::string key = Util::urlDecode(str.substr(0, pos), true);
        std::string val = Util::urlDecode(str.substr(pos + 1), true);
        m_request.setQuery(key, val);
    }
    // [4] 协议版本的获取
    m_request.m_version = matches[4];
    return true;
}
bool HttpContext::recvRequestLine(Buffer *buf)
{
    if (m_recvStatu != HttpRecvStatu::RECV_REQUEST_LINE)
    {
        return false;
    }
    // 1. 获取一行数据，带有末尾的换行
    std::string line = buf->readLineString();
    // 2. 需要考虑的一些要素：缓冲区中的数据不足一行， 获取的一行数据过大
    if (line.size() == 0)
    {
        // 缓冲区中的数据不足一行，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，这是有问题的
        if (buf->getReadSize() > MAX_LINE)
        {
            m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
            m_respStatu = 414; // URI TOO LONG
            return false;
        }
        // 缓冲区中数据不足一行，但是也不多，就等等新数据的到来
        return true;
    }
    if (line.size() > MAX_LINE)
    {
        m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
        m_respStatu = 414; // URI TOO LONG
        return false;
    }
    // 3. 获取完毕，解析请求行
    bool ret = parseRequestLine(line);
    if (ret == false)
    {
        return false;
    }   
    // 首行处理完毕，进入头部获取阶段
    m_recvStatu = HttpRecvStatu::RECV_REQUEST_HEAD;
    return true;
}
//
bool HttpContext::parseRequestHead(std::string &line)
{
    if (line.back() == '\n')
    {
        line.pop_back(); // 末尾是换行则去掉换行字符
    }
    if (line.back() == '\r')
    {
        line.pop_back(); // 末尾是回车则去掉回车字符
    }
    size_t pos = line.find(": ");
    if (pos == std::string::npos)
    {
        m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
        m_respStatu = 400; //
        return false;
    }
    std::string key = line.substr(0, pos);
    std::string val = line.substr(pos + 2);
    m_request.setHeader(key, val);
    return true;
}
bool HttpContext::recvRequestHead(Buffer *buf)
{
    if (m_recvStatu != HttpRecvStatu::RECV_REQUEST_HEAD)
    {
        return false;
    }
    // 1. 一行一行取出数据，直到遇到空行为止， 头部的格式 key: val\r\nkey: val\r\n....
    while (1)
    {
        std::string line = buf->readLineString();
        // 2. 需要考虑的一些要素：缓冲区中的数据不足一行， 获取的一行数据超大
        if (line.size() == 0)
        {
            // 缓冲区中的数据不足一行，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，这是有问题的
            if (buf->getReadSize() > MAX_LINE)
            {
                m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
                m_respStatu = 414; // URI TOO LONG
                return false;
            }
            // 缓冲区中数据不足一行，但是也不多，就等等新数据的到来
            return true;
        }
        if (line.size() > MAX_LINE)
        {
            m_recvStatu = HttpRecvStatu::RECV_REQUEST_ERROR;
            m_respStatu = 414; // URI TOO LONG
            return false;
        }
        if (line == "\n" || line == "\r\n")
        {
            break;
        }
        bool ret = parseRequestHead(line);
        if (ret == false)
        {
            return false;
        }
    }
    // 头部处理完毕，进入正文获取阶段
    m_recvStatu = HttpRecvStatu::RECV_REQUEST_BODY;
    return true;
}
bool HttpContext::recvRequestBody(Buffer *buf)
{
    if (m_recvStatu != HttpRecvStatu::RECV_REQUEST_BODY)
    {
        return false;
    }
    // 1. 获取正文长度
    size_t content_length = m_request.contentLength();
    if (content_length == 0)
    {
        // 没有正文，则请求接收解析完毕
        m_recvStatu = HttpRecvStatu::RECV_REQUEST_OVER;
        return true;
    }
    // 2. 当前已经接收了多少正文,其实就是往_request._body 中放了多少数据了
    size_t real_len = content_length - m_request.getBody().size(); // 实际还需要接收的正文长度
    // 3. 接收正文放到body中，但是也要考虑当前缓冲区中的数据，是否是全部的正文
    //   3.1 缓冲区中数据，包含了当前请求的所有正文，则取出所需的数据
    if (buf->getReadSize() >= real_len)
    {
        m_request.setBody(buf->getReadAddr(), real_len);
        buf->moveReadStep(real_len);
        m_recvStatu = HttpRecvStatu::RECV_REQUEST_OVER;
        return true;
    }
    //  3.2 缓冲区中数据，无法满足当前正文的需要，数据不足，取出数据，然后等待新数据到来
    m_request.setBody(buf->getReadAddr(), buf->getReadSize());
    buf->moveReadStep(buf->getReadSize());
    return true;
}

HttpContext::HttpContext() 
    : m_respStatu(200), m_recvStatu(HttpRecvStatu::RECV_REQUEST_LINE)
{
}
void HttpContext::reset()
{
    m_respStatu = 200;
    m_recvStatu = HttpRecvStatu::RECV_REQUEST_LINE;
    m_request.reset();
}
int HttpContext::respStatu()
{
    return m_respStatu;
}
HttpRecvStatu HttpContext::recvStatu()
{
    return m_recvStatu;
}
HttpRequest &HttpContext::request()
{
    return m_request;
}
// 接收并解析HTTP请求
bool HttpContext::recvHttpRequest(Buffer *buf)
{
    // 不同的状态，做不同的事情，但是这里不要break,因为处理完请求行后，应该立即处理头部，而不是退出等新数据
    switch (m_recvStatu)
    {
    case HttpRecvStatu::RECV_REQUEST_LINE:
        if (recvRequestLine(buf) == false)
        {
            return false;
        }
    case HttpRecvStatu::RECV_REQUEST_HEAD:
        if (recvRequestHead(buf) == false)
        {
            return false;
        }
    case HttpRecvStatu::RECV_REQUEST_BODY:
        if (recvRequestBody(buf) == false)
        {
            return false;
        }
    }
    return true;
}
